package ohc.login

import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.text.Text
import net.minecraft.util.Formatting
import java.util.*
import java.util.concurrent.ConcurrentHashMap

/**
 * 登录状态管理类
 */
object LoginManager {
    // 存储未登录玩家的状态
    // Key: 玩家 UUID, Value: 登录状态
    private val pendingLogins: ConcurrentHashMap<UUID, LoginState> = ConcurrentHashMap()
    
    // 存储玩家密码错误记录（用于冷却和踢出）
    // Key: 玩家 UUID, Value: 密码错误状态
    private val wrongPasswordRecords: ConcurrentHashMap<UUID, WrongPasswordState> = ConcurrentHashMap()
    
    // 存储被封禁的玩家
    // Key: 玩家 UUID, Value: 封禁解除时间（毫秒时间戳）
    private val bannedPlayers: ConcurrentHashMap<UUID, Long> = ConcurrentHashMap()
    
    // 存储被封禁的 UID
    // Key: API UID, Value: 封禁信息
    data class UidBanInfo(
        val banUntil: Long,      // 封禁解除时间
        val reason: String?      // 封禁原因
    )
    private val bannedUids: ConcurrentHashMap<Long, UidBanInfo> = ConcurrentHashMap()
    
    // API 连接状态
    @Volatile
    private var apiAvailable: Boolean = true
    
    // 上次 API 状态检查时间
    @Volatile
    private var lastApiCheckTime: Long = 0
    
    // API 状态检查间隔（毫秒）
    private const val API_CHECK_INTERVAL = 30_000L
    
    // 存储通过备用登录的玩家（用于管理员操作后强制重新登录）
    private val backupLoggedPlayers: MutableSet<UUID> = ConcurrentHashMap.newKeySet()
    
    /**
     * 登录状态数据类
     */
    data class LoginState(
        val uuid: UUID,
        val joinTime: Long,         // 进入服务器时间
        val needBind: Boolean       // 是否需要绑定（首次登录）
    )
    
    /**
     * 密码错误状态数据类
     */
    data class WrongPasswordState(
        var wrongAttempts: Int = 0,           // 密码错误次数（用于冷却）
        var totalWrongAttempts: Int = 0,      // 总密码错误次数（用于踢出）
        var cooldownUntil: Long = 0           // 冷却结束时间（毫秒时间戳）
    )
    
    /**
     * 玩家进入服务器时调用
     * @param player 玩家实体
     */
    fun onPlayerJoin(player: ServerPlayerEntity) {
        val uuid = player.uuid
        val needBind = !PlayerDataManager.isBound(uuid)
        
        // 检查已绑定玩家的 UID 是否被封禁
        if (!needBind) {
            val playerData = PlayerDataManager.getPlayerData(uuid)
            if (playerData != null) {
                val uidBanInfo = getUidBanInfo(playerData.apiUid)
                if (uidBanInfo != null) {
                    val (remainingHours, reason) = uidBanInfo
                    val kickMsg = buildString {
                        append("§c您的账号(UID: ${playerData.apiUid})已被封禁")
                        if (remainingHours != null) append("\n§c剩余: ${String.format("%.1f", remainingHours)} 小时")
                        else append("（永久）")
                        if (!reason.isNullOrBlank()) append("\n§e原因: $reason")
                    }
                    player.networkHandler.disconnect(Text.literal(kickMsg))
                    Ohc_login.logger.info("玩家 ${player.name.string} 因 UID ${playerData.apiUid} 被封禁而被踢出")
                    return
                }
            }
        }
        
        pendingLogins[uuid] = LoginState(
            uuid = uuid,
            joinTime = System.currentTimeMillis(),
            needBind = needBind
        )
        
        // 异步检测 API 状态后再发送提示
        ApiService.checkApiAvailableAsync().thenAccept { available ->
            (player.getEntityWorld() as net.minecraft.server.world.ServerWorld).server.execute {
                if (player.isRemoved) return@execute
                
                // 更新 API 状态
                setApiAvailable(available)
                
                // 发送提示消息
                if (needBind) {
                    val hasTempAccount = PlayerDataManager.hasTempAccount(uuid)
                    
                    player.sendMessage(
                        Text.literal("§e欢迎来到服务器！").formatted(Formatting.YELLOW)
                    )
                    
                    if (available) {
                        // API 可用
                        player.sendMessage(
                            Text.literal("§a请使用 §f/bind <账号> <密码> §a将您的角色绑定到账号。").formatted(Formatting.GREEN)
                        )
                        // 如果有临时账号，提示绑定后会删除
                        if (hasTempAccount) {
                            player.sendMessage(
                                Text.literal("§eAPI 已恢复！请使用 §f/bind §e绑定正式账号，绑定后临时账号将自动删除。").formatted(Formatting.YELLOW)
                            )
                        }
                    } else {
                        // API 不可用
                        player.sendMessage(
                            Text.literal("§cAPI 当前不可用！").formatted(Formatting.RED)
                        )
                        if (hasTempAccount) {
                            player.sendMessage(
                                Text.literal("§e您有临时账号，请使用 §f/dl <密码> §e登录。").formatted(Formatting.YELLOW)
                            )
                        } else {
                            player.sendMessage(
                                Text.literal("§e您可以使用 §f/r <密码> §e创建临时账号。").formatted(Formatting.YELLOW)
                            )
                            player.sendMessage(
                                Text.literal("§e或等待 API 恢复后使用 §f/bind §e绑定正式账号。").formatted(Formatting.YELLOW)
                            )
                        }
                    }
                } else {
                    val playerData = PlayerDataManager.getPlayerData(uuid)
                    player.sendMessage(
                        Text.literal("§e欢迎回来！您的账号已绑定到 UID: ${playerData?.apiUid ?: "未知"}").formatted(Formatting.YELLOW)
                    )
                    
                    if (available) {
                        player.sendMessage(
                            Text.literal("§a请使用 §f/l <密码> §a登录。").formatted(Formatting.GREEN)
                        )
                    } else {
                        // API 不可用
                        player.sendMessage(
                            Text.literal("§cAPI 当前不可用！").formatted(Formatting.RED)
                        )
                        if (PlayerDataManager.hasBackupPassword(uuid)) {
                            player.sendMessage(
                                Text.literal("§e请使用 §f/dl <备用密码> §e进行备用登录。").formatted(Formatting.YELLOW)
                            )
                        } else {
                            player.sendMessage(
                                Text.literal("§e您可以使用 §f/r <密码> §e创建备用密码。").formatted(Formatting.YELLOW)
                            )
                            player.sendMessage(
                                Text.literal("§e或等待 API 恢复").formatted(Formatting.YELLOW)
                            )
                        }
                    }
                }
                
                player.sendMessage(
                    Text.literal("§c在登录之前，您无法移动或进行任何操作。").formatted(Formatting.RED)
                )
                
                // 显示登录倒计时
                player.sendMessage(
                    Text.literal("§e请在 ${Config.loginTimeout} 秒内登录").formatted(Formatting.YELLOW),
                    true  
                )
            }
        }
        
        Ohc_login.logger.info("玩家 ${player.name.string} 进入服务器，等待${if (needBind) "绑定" else "登录"}")
    }
    
    /**
     * 玩家离开服务器时调用
     * @param player 玩家实体
     */
    fun onPlayerLeave(player: ServerPlayerEntity) {
        pendingLogins.remove(player.uuid)
    }
    
    /**
     * 检查玩家是否已登录
     * @param uuid 玩家 UUID
     * @return 是否已登录
     */
    fun isLoggedIn(uuid: UUID): Boolean {
        return !pendingLogins.containsKey(uuid)
    }
    
    /**
     * 检查玩家是否需要绑定
     * @param uuid 玩家 UUID
     * @return 是否需要绑定
     */
    fun needsBind(uuid: UUID): Boolean {
        return pendingLogins[uuid]?.needBind ?: false
    }
    
    /**
     * 获取玩家的登录状态
     * @param uuid 玩家 UUID
     * @return 登录状态，如果已登录则返回 null
     */
    fun getLoginState(uuid: UUID): LoginState? {
        return pendingLogins[uuid]
    }
    
    /**
     * 完成登录
     * @param uuid 玩家 UUID
     */
    fun completeLogin(uuid: UUID) {
        pendingLogins.remove(uuid)
        backupLoggedPlayers.remove(uuid)  // 正常登录时清除备用登录标记
        PlayerDataManager.updateLastLoginTime(uuid)
    }
    
    /**
     * 完成备用登录（标记为备用登录状态）
     * @param uuid 玩家 UUID
     */
    fun completeBackupLogin(uuid: UUID) {
        pendingLogins.remove(uuid)
        backupLoggedPlayers.add(uuid)
        PlayerDataManager.updateLastLoginTime(uuid)
    }
    
    /**
     * 检查玩家是否通过备用登录
     * @param uuid 玩家 UUID
     * @return 是否通过备用登录
     */
    fun isBackupLogged(uuid: UUID): Boolean {
        return backupLoggedPlayers.contains(uuid)
    }
    
    /**
     * 强制玩家重新登录（用于管理员操作后）
     * @param player 玩家实体
     */
    fun forceRelogin(player: ServerPlayerEntity) {
        val uuid = player.uuid
        backupLoggedPlayers.remove(uuid)
        onPlayerJoin(player)
    }
    
    /**
     * 检查是否超时
     * @param uuid 玩家 UUID
     * @return 是否超时
     */
    fun isTimeout(uuid: UUID): Boolean {
        val state = pendingLogins[uuid] ?: return false
        val elapsed = (System.currentTimeMillis() - state.joinTime) / 1000
        return elapsed >= Config.loginTimeout
    }
    
    /**
     * 检查玩家是否被封禁
     * @param uuid 玩家 UUID
     * @return 如果被封禁返回剩余封禁时间（小时），否则返回 null
     */
    fun getBanRemainingHours(uuid: UUID): Double? {
        val banUntil = bannedPlayers[uuid] ?: return null
        val now = System.currentTimeMillis()
        if (now >= banUntil) {
            bannedPlayers.remove(uuid)
            return null
        }
        return (banUntil - now) / 1000.0 / 3600.0
    }
    
    /**
     * 检查玩家是否在冷却中
     * @param uuid 玩家 UUID
     * @return 如果在冷却中返回剩余冷却时间（秒），否则返回 null
     */
    fun getCooldownRemainingSeconds(uuid: UUID): Int? {
        val record = wrongPasswordRecords[uuid] ?: return null
        val now = System.currentTimeMillis()
        if (now >= record.cooldownUntil) {
            return null
        }
        return ((record.cooldownUntil - now) / 1000).toInt()
    }
    
    /**
     * 记录密码错误
     * @param uuid 玩家 UUID
     * @return 返回处理结果："cooldown" 表示触发冷却，"kick" 表示需要踢出，null 表示正常
     */
    fun recordWrongPassword(uuid: UUID): WrongPasswordResult {
        val record = wrongPasswordRecords.getOrPut(uuid) { WrongPasswordState() }
        record.wrongAttempts++
        record.totalWrongAttempts++
        
        // 检查是否需要踢出
        if (record.totalWrongAttempts >= Config.wrongPasswordKickAttempts) {
            // 设置封禁时间
            val banUntil = System.currentTimeMillis() + Config.wrongPasswordKickHours * 3600 * 1000L
            bannedPlayers[uuid] = banUntil
            wrongPasswordRecords.remove(uuid)
            return WrongPasswordResult.KICK
        }
        
        // 检查是否需要冷却
        if (record.wrongAttempts >= Config.wrongPasswordCooldownAttempts) {
            record.cooldownUntil = System.currentTimeMillis() + Config.wrongPasswordCooldownSeconds * 1000L
            record.wrongAttempts = 0  // 重置冷却计数
            return WrongPasswordResult.COOLDOWN
        }
        
        return WrongPasswordResult.NORMAL
    }
    
    /**
     * 清除玩家的密码错误记录（登录成功时调用）
     * @param uuid 玩家 UUID
     */
    fun clearWrongPasswordRecord(uuid: UUID) {
        wrongPasswordRecords.remove(uuid)
    }
    
    /**
     * 获取所有被封禁的玩家
     * @return Map<UUID, 剩余封禁时间（小时）>
     */
    fun getBannedPlayers(): Map<UUID, Double> {
        val now = System.currentTimeMillis()
        // 清理已过期的封禁
        bannedPlayers.entries.removeIf { it.value <= now }
        return bannedPlayers.mapValues { (_, banUntil) ->
            (banUntil - now) / 1000.0 / 3600.0
        }
    }
    
    /**
     * 解除玩家封禁
     * @param uuid 玩家 UUID
     * @return 是否成功解除（玩家是否在封禁列表中）
     */
    fun unbanPlayer(uuid: UUID): Boolean {
        return bannedPlayers.remove(uuid) != null
    }
    
    /**
     * 封禁玩家
     * @param uuid 玩家 UUID
     * @param hours 封禁时长（小时）
     */
    fun banPlayer(uuid: UUID, hours: Double = Config.wrongPasswordKickHours.toDouble()) {
        val banUntil = System.currentTimeMillis() + (hours * 3600 * 1000).toLong()
        bannedPlayers[uuid] = banUntil
    }
    
    /**
     * 封禁 UID
     * @param uid API UID
     * @param hours 封禁时长（小时），为 null 表示永久封禁
     * @param reason 封禁原因
     */
    fun banUid(uid: Long, hours: Double? = null, reason: String? = null) {
        val banUntil = if (hours != null) {
            System.currentTimeMillis() + (hours * 3600 * 1000).toLong()
        } else {
            Long.MAX_VALUE  // 永久封禁
        }
        bannedUids[uid] = UidBanInfo(banUntil, reason)
        PlayerDataManager.save()  // 立即保存
    }
    
    /**
     * 解除 UID 封禁
     * @param uid API UID
     * @return 是否成功解除
     */
    fun unbanUid(uid: Long): Boolean {
        val result = bannedUids.remove(uid) != null
        if (result) PlayerDataManager.save()  // 立即保存
        return result
    }
    
    /**
     * 检查 UID 是否被封禁
     * @param uid API UID
     * @return 封禁信息（剩余小时数和原因），未封禁返回 null
     */
    fun getUidBanInfo(uid: Long): Pair<Double?, String?>? {
        val info = bannedUids[uid] ?: return null
        val now = System.currentTimeMillis()
        if (now >= info.banUntil) {
            bannedUids.remove(uid)
            return null
        }
        val remainingHours = if (info.banUntil == Long.MAX_VALUE) null else (info.banUntil - now) / 1000.0 / 3600.0
        return Pair(remainingHours, info.reason)
    }
    
    /**
     * 获取所有被封禁的 UID
     * @return Map<UID, Pair<剩余小时数(null表示永久), 原因>>
     */
    fun getBannedUids(): Map<Long, Pair<Double?, String?>> {
        val now = System.currentTimeMillis()
        bannedUids.entries.removeIf { it.value.banUntil <= now }
        return bannedUids.mapValues { (_, info) ->
            val remainingHours = if (info.banUntil == Long.MAX_VALUE) null else (info.banUntil - now) / 1000.0 / 3600.0
            Pair(remainingHours, info.reason)
        }
    }
    
    /**
     * 获取 UID 封禁数据用于保存
     */
    fun getBannedUidsForSave(): MutableMap<String, PlayerDataManager.UidBanData> {
        val now = System.currentTimeMillis()
        bannedUids.entries.removeIf { it.value.banUntil <= now }
        return bannedUids.mapKeys { it.key.toString() }
            .mapValues { PlayerDataManager.UidBanData(it.value.banUntil, it.value.reason) }
            .toMutableMap()
    }
    
    /**
     * 从保存的数据加载 UID 封禁
     */
    fun loadBannedUids(data: MutableMap<String, PlayerDataManager.UidBanData>) {
        bannedUids.clear()
        val now = System.currentTimeMillis()
        data.forEach { (uidStr, banData) ->
            val uid = uidStr.toLongOrNull() ?: return@forEach
            if (banData.banUntil > now) {
                bannedUids[uid] = UidBanInfo(banData.banUntil, banData.reason)
            }
        }
    }
    
    /**
     * 密码错误处理结果
     */
    enum class WrongPasswordResult {
        NORMAL,    // 正常，继续尝试
        COOLDOWN,  // 触发冷却
        KICK       // 需要踢出并封禁
    }
    
    /**
     * 获取所有待登录的玩家 UUID
     * @return UUID 集合
     */
    fun getPendingPlayers(): Set<UUID> {
        return pendingLogins.keys.toSet()
    }
    
    /**
     * 获取剩余登录时间（秒）
     * @param uuid 玩家 UUID
     * @return 剩余时间，如果已登录则返回 -1
     */
    fun getRemainingTime(uuid: UUID): Int {
        val state = pendingLogins[uuid] ?: return -1
        val elapsed = (System.currentTimeMillis() - state.joinTime) / 1000
        return (Config.loginTimeout - elapsed).toInt().coerceAtLeast(0)
    }
    
    /**
     * 获取玩家剩余密码错误次数（到踢出）
     * @param uuid 玩家 UUID
     * @return 剩余次数
     */
    fun getRemainingWrongAttempts(uuid: UUID): Int {
        val record = wrongPasswordRecords[uuid] ?: return Config.wrongPasswordKickAttempts
        return (Config.wrongPasswordKickAttempts - record.totalWrongAttempts).coerceAtLeast(0)
    }
    
    /**
     * 设置 API 可用状态
     */
    fun setApiAvailable(available: Boolean) {
        if (apiAvailable != available) {
            apiAvailable = available
            Ohc_login.logger.info("API 状态已更新: ${if (available) "可用" else "不可用"}")
        }
        lastApiCheckTime = System.currentTimeMillis()
    }
    
    /**
     * 检查 API 是否可用
     */
    fun isApiAvailable(): Boolean = apiAvailable
    
    /**
     * 检查是否需要重新检查 API 状态
     */
    fun needsApiCheck(): Boolean {
        return System.currentTimeMillis() - lastApiCheckTime > API_CHECK_INTERVAL
    }
}
